package com.hollycrm.hollysqm.core.index.dao.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.HighlightParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import com.hollycrm.hollysqm.core.index.dao.IndexSearchDao;
import com.hollycrm.hollysqm.core.util.DocToBeanUtil;
import com.hollycrm.hollysqm.core.util.SolrUtils;
import com.hollycrm.hollysqm.core.vo.I8DocBean;
import com.hollycrm.hollysqm.core.vo.V8DocBean;
import com.hollycrm.hollysqm.util.Constant;

/**
 * Solr索引查询类
 * @author jianglong
 * @date 2017年2月22日 下午3:49:54
 */
@Repository
public class IndexSearchDaoImpl implements IndexSearchDao {
	/**
	 * 日志
	 */
	private static final Logger log = LoggerFactory.getLogger(IndexSearchDaoImpl.class);
	/**
	 * 最小行数
	 */	
	private static final int MIN_ROWS = 1;
	/**
	 * 高亮样式
	 */
	private static final String HIGHLIGHT_PRE ="<span style='color:red;'>";
    private static final String HIGHLIGHT_POST ="</span>";
    /**
     * 高亮字段
     */
    private String highlightField = null;
    /**
     * 高亮摘要字数
     */
    private int highlightFragsize = 9999;
    
    /**
     * Solr查询的索引集名称
     */
    private String collection;
    
	/**
     * 添加高亮参数
     * @param sq 查询参数
     */
    private void addHighlightParams(SolrQuery sq){
        sq.setHighlight(true);
        //如果未设定高字段，则使用默认高亮字段；        
        if (StringUtils.isBlank(highlightField))
        	this.setHighlightField(Constant.allContent);
        sq.set(HighlightParams.FIELDS, highlightField);
        sq.setHighlightSimplePre(HIGHLIGHT_PRE);
        sq.setHighlightSimplePost(HIGHLIGHT_POST);
        sq.setHighlightRequireFieldMatch(false);//默认值是false，匹配某个字段却高亮一个不同的字段，否则按角色查询时，无法高亮        
        sq.setHighlightFragsize(highlightFragsize);//设置摘要个数,返回长度 10万字 够长了吧？
    }
    
    /**
     * 设置高亮显示字段
     * @param field
     */
    public void setHighlightField(String field){
    	highlightField = field;
    }
    
    /**
     * 设置高亮显示字段的摘要字数
     * @param field
     */
    public void setHighlightFragsize(int fragsize){
    	highlightFragsize = fragsize;
    }
    
	/**
	 * 设置Solr索引集
	 * @param collection
	 */
	public void setCollection(String collection) {
		this.collection = collection;
	}

	/**
     * 生成SolrQuery
     * @param qs 查询条件
     * @param fqs 过滤条件
     * @return 返回查询参数
     */
    private SolrQuery createSolrQuery(String qs,String[] fqs){
        SolrQuery sq=new SolrQuery();
        if(StringUtils.isEmpty(qs))//如果qs为null则默认查询全部
            qs="*";
        if(qs.trim().startsWith("NOT")) //如果以not开头，则认为是在所有的基础上进行not的
            qs = "* "+qs;
        sq.setQuery(qs);
        sq.setFilterQueries(fqs);
        log.info("Solr -> \r\nqs ["+qs+"],\r\nfqs["+StringUtils.join(fqs,",")+"]");
//        sq.set("df",Constant.allContent);//默认查询字段
        return sq;
    }
	
	/**
     * solrCloud查询
     * @param sq 查询参数
     * @return solr响应
     * @throws IOException
     * @throws SolrServerException
     */
    private QueryResponse query(SolrQuery sq) throws IOException,SolrServerException{
        SolrClient client = getSolrClient();
        return client.query(sq, SolrRequest.METHOD.POST);//post方式提交 防止提交数据过大而导致的失败
    }
	
    /**
     * 返回查询结果
     * @param qs
     * @param paramMap
     * @param clzss
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
	public <T> List<T> queryResult(String qs, Map<String,String> paramMap,Class<T> clzss) throws IOException,SolrServerException{
		return this.queryResults(qs, paramMap, 0, 0, false, clzss);
	}
	
	/**
     * 返回查询结果ID集合
     * @param qs
     * @param paramMap
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
	public List<String> queryResultIds(String qs, Map<String,String> paramMap) throws IOException,SolrServerException{
		SolrQuery sq = createSolrQuery(qs, getParamFqs(paramMap));	
		sq.setStart(0);
		sq.setRows(100000);//最大返回10万
		QueryResponse qr = query(sq);		
		SolrDocumentList records = qr.getResults();
		log.info("solr query size:"+ records.size());
		List<String> list = new ArrayList<String>();
		for (SolrDocument record : records) {
			list.add((String)record.get(Constant.id));
		}		
		return list;
	}
	
	/**
	 * 返回查询结果ID集合
	 * @param qs
	 * @param paramMap
	 * @param collection
	 * @param sortField
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public List<String> queryResultIds(String qs, Map<String,String> paramMap,final String collection,final String sortField) throws IOException,SolrServerException{
		SolrQuery sq = createSolrQuery(qs, getParamFqs(paramMap));	
		sq.setStart(0);
		sq.setRows(100000);//最大返回10万
//		QueryResponse qr = query(sq);	
		SolrClient client = getSolrClient(collection);
		sq.addSort(sortField, SolrQuery.ORDER.desc);
		
//		SolrClient client = SolrUtils.getSolrClient();		
//		if (tag.equals("v8")){
//			SolrUtils.setDefaultCollection("hollysqm_v8");
//			sq.addSort(Constant.acceptTime, SolrQuery.ORDER.desc);
//		}else if (tag.equals("i8")){
//			SolrUtils.setDefaultCollection("hollysqm_i8");
//			sq.addSort(Constant.contactTime, SolrQuery.ORDER.desc);
//		}else {
//			return null;
//		}
        sq.addSort(Constant.id, SolrQuery.ORDER.asc);
		QueryResponse qr = client.query(sq, SolrRequest.METHOD.POST);//post方式提交 防止提交数据过大而导致的失败
        
		SolrDocumentList records = qr.getResults();
		log.info("solr query size:"+ records.size());
		List<String> list = new ArrayList<String>();
		for (SolrDocument record : records) {
			list.add((String)record.get(Constant.id));
		}		
		return list;
	}
	
	/**
	 * 返回查询结果，带高亮显示
	 * @param qs
	 * @param paramMap
	 * @param isHighlight
	 * @param clzss
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public <T> List<T> queryResult(String qs, Map<String,String> paramMap,boolean isHighlight,Class<T> clzss) throws IOException,SolrServerException{		
		return this.queryResults(qs, paramMap, 0, 0, isHighlight, clzss);
	}
	
	/**
	 * 返回查询结果，带高亮显示,带分页查询
	 * @param qs
	 * @param paramMap
	 * @param start
	 * @param rows
	 * @param isHighlight
	 * @param clzss
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public <T> List<T> queryResult(String qs, Map<String,String> paramMap,int start,int rows,boolean isHighlight,Class<T> clzss) throws IOException,SolrServerException{			
		return this.queryResults(qs, paramMap, start, rows, isHighlight, clzss);
	}
	
	/**
	 * 通过查询方法
	 * @param qs
	 * @param paramMap
	 * @param start
	 * @param rows
	 * @param isHighlight
	 * @param clzss
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	private <T> List<T> queryResults(String qs, Map<String,String> paramMap,int start,int rows,boolean isHighlight,Class<T> clzss) throws IOException,SolrServerException{
		SolrQuery sq = createSolrQuery(qs, getParamFqs(paramMap));
		if (start>=0 && rows>=1){
			sq.setStart((start - 1) * rows);//游标查询不能设置其实页
	        sq.setRows(rows);
	        if (clzss.isAssignableFrom(V8DocBean.class)){
	        	sq.addSort(Constant.acceptTime, SolrQuery.ORDER.desc);
	        }else if (clzss.isAssignableFrom(I8DocBean.class)){
	        	sq.addSort(Constant.contactTime, SolrQuery.ORDER.desc);
	        }
	        sq.addSort(Constant.id, SolrQuery.ORDER.asc);
		}else {
			sq.setStart(0);//游标查询不能设置其实页
	        sq.setRows(100000);
		}
        if (isHighlight){//高亮查询			
            return DocToBeanUtil.docToBeanList(queryHighlighting(sq),clzss);			
        }
		QueryResponse qr = query(sq);
		return DocToBeanUtil.docToBeanList(qr.getResults(),clzss);
	}
	
	/**
	 * 高亮查询文本数据
	 * @param sq
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	private SolrDocumentList queryHighlighting(SolrQuery sq) throws IOException,SolrServerException {
		addHighlightParams(sq);//设置高亮
		QueryResponse qr = query(sq);
		SolrDocumentList list = qr.getResults();
		String id = null;
        for(SolrDocument doc: list){        	
            id = (String) doc.getFieldValue(Constant.id);
            if(id!=null && qr.getHighlighting().get(id)!=null && qr.getHighlighting().get(id).get(highlightField)!=null){                	
            	log.debug("solr highlight >> " + doc.get(id) +"\t"+ qr.getHighlighting().get(id).get(highlightField));
            	doc.setField(Constant.allContent, StringUtils.join(qr.getHighlighting().get(id).get(highlightField),""));
            }
        }
		return list;
	}	
	
	/**
	 * 通过ID查询Solr获取指定类型返回对象
	 * @param id 主键
	 * @param clzss 类型
	 * @return T
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public <T>T getById(final String id,final Class<T> clzss) throws IOException,SolrServerException{		
		QueryResponse qr = query(this.createSolrQuery(String.format("%s:%s", Constant.id,id), null).setRows(MIN_ROWS));
		SolrDocumentList docList = qr.getResults();
		if (docList == null || docList.size()<=0) return null;
		return DocToBeanUtil.docToAnnBean(docList.get(0), clzss);
	}	
	
	/**
	 * 增加文档
	 * @param doc
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public void addDoc(SolrInputDocument doc) throws IOException,SolrServerException{
		SolrClient client = getSolrClient();
		client.add(doc);
		client.commit();
		log.info("solr -> add doc id: "+doc.getFieldValue(Constant.id));
	}
	
	/**
	 * 增加文档
	 * @param doc
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public void addDocs(Collection<SolrInputDocument> docs) throws IOException,SolrServerException{
		SolrClient client = getSolrClient();
		client.add(docs);
		client.commit();
		log.info("solr -> add doc size: "+docs.size());
	}
	
	/**
	 * 将指定的对象更新到Solr中
	 * @param clzss
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public <T> void updateDoc(final T clzss)  throws IOException,SolrServerException{
		SolrInputDocument doc = DocToBeanUtil.beanToAnnDoc(clzss);
		updateDoc(doc);
	}
	
	/**
	 * 更新指定文档
	 * @param doc
	 * @param clzss
	 * @throws IOException
	 * @throws SolrServerException
	 */
	private <T> void updateDoc(SolrInputDocument doc)  throws IOException,SolrServerException{
		String id = (String)doc.getFieldValue(Constant.id);
		QueryResponse qr = query(this.createSolrQuery(String.format("%s:%s", Constant.id,id), null).setRows(MIN_ROWS));
		if (qr!=null)//先删除后增加
			deleteById(id);
		addDoc(doc);
	}
	
	/**
	 * 通过主键ID删除文档
	 * @param id
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public void deleteById(String id) throws IOException,SolrServerException{
		SolrClient client = getSolrClient();
		client.deleteById(id);
		client.commit();
		log.info("solr -> delete doc id: "+id);
	}
	
	/**
	 * 统计查询结果总数
	 * @param qs
	 * @param paramMap
	 * @return
	 * @throws IOException
	 * @throws SolrServerException
	 */
	public int count(final String qs,final Map<String,String> paramMap) throws IOException,SolrServerException{
		QueryResponse qr = this.query(this.createSolrQuery(qs, this.getParamFqs(paramMap)).setRows(MIN_ROWS));
		int count = (int)qr.getResults().getNumFound();
		log.info("solr -> count : "+count);
		return count;
	}
	
	/**
	 * 将Map过滤参数转换成数组
	 * @param paramMap
	 * @return
	 */
	private String[] getParamFqs(final Map<String,String> paramMap){		
		String[] params = new String[paramMap.size()];
		int i=0;
		for (Entry<String,String> entry : paramMap.entrySet()){
			params[i++] = String.format("%s:%s", entry.getKey(), entry.getValue());			
		}		
		return params;
	}
	
	/**
	 * 获取索引集对象
	 * @return
	 */
	private SolrClient getSolrClient(){
		return getSolrClient(collection);
	}
	
	/**
	 * 获取索引集对象,设置自定义集合
	 * @param collection
	 * @return
	 */
	private SolrClient getSolrClient(String collection){
		SolrClient solrClient = SolrUtils.getSolrClient();
		if (StringUtils.isNotBlank(collection))
			SolrUtils.setDefaultCollection(collection);
		return solrClient;
	}
}
